package com.qingcloud.adminbackend.controller;

import cn.hutool.core.img.ImgUtil;
import com.qingcloud.adminbackend.base.AjaxResult;
import com.qingcloud.adminbackend.constant.WamingConstant;
import com.qingcloud.adminbackend.entity.config.SysCommonConfig;
import com.qingcloud.adminbackend.mapper.SysCommonConfigMapper;
import com.qingcloud.adminbackend.utils.DateUtil;
import com.qingcloud.adminbackend.utils.RequestUtil;
import io.swagger.annotations.*;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 文件管理
 * </p>
 *
 * @author liyuhong
 * @since 2018-11-15
 */
@Api(value = "文件上传下载API", description = "文件上传下载API")
@RestController
@RequestMapping("/file")
public class FileController {

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(FileController.class);

    @Value("${file.rootpath}")
    private String ROOTPATH;

    @Resource
    private SysCommonConfigMapper sysCommonConfigMapper;

    /**
     * 单个文件上传
     */
    @ApiOperation(value = "单个文件上传", notes = "单个文件上传")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "前端框架文件ID",
                    required = false, dataType = "string", paramType = "form", defaultValue = "11")
    })
    @ResponseBody
    @PostMapping(value = "/upload", consumes = "multipart/*", headers = "content-type=multipart/form-data")
    public AjaxResult upload(@ApiParam("上传的文件") MultipartFile file, @RequestParam("id") String fileId,
                             MultipartHttpServletRequest request) throws IOException {
        String newFileName = UUID.randomUUID().toString().replace("-", "");
        String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        newFileName = newFileName + fileSuffix;
        String rootPath = ROOTPATH;
        String path = LocalDate.now().format(DateUtil.CHINESE_DATE_YMD_2);
        Path dir = Paths.get(rootPath + path);
        if (!Files.exists(dir)) {
            Files.createDirectories(dir);
        }
        File newFile = new File(dir.toString(), newFileName);
        try (OutputStream os = new FileOutputStream(newFile)) {
            FileCopyUtils.copy(file.getInputStream(), os);
            os.flush();
        }
        AjaxResult result = new AjaxResult();
        String url = path + "/" + newFileName;
        Map<String, String> resultMap = new HashMap<>(3);
        resultMap.put("fileId", fileId);
        resultMap.put("urlPrefix", request.getContextPath() + "/file/view/");
        resultMap.put("url", url);
        //生产MD5码
        String md5 = DigestUtils.md5Hex(file.getInputStream());
        resultMap.put("md5", md5);
        result.setResult(resultMap);
        result.setErroCode(2000);
        return result;
    }

    @ApiOperation(value = "访问文件", notes = "访问文件")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "day", value = "文件日期",
                    dataType = "string", paramType = "path", defaultValue = "20180412"),
            @ApiImplicitParam(name = "fileName", value = "文件名称",
                    dataType = "string", paramType = "path", defaultValue = "231nksdnfkas.jpg"),
            @ApiImplicitParam(name = "flag", value = "访问模式，1下载，2浏览",
                    dataType = "int", paramType = "query", defaultValue = "1"),
            @ApiImplicitParam(name = "cut", value = "0不截取 1等宽高截取",
                    dataType = "int", paramType = "query", defaultValue = "0"),
    })
    @GetMapping(value = "/view/{day}/{fileName}")
    public void view(@PathVariable("day") String day, @PathVariable("fileName") String fileName,
                     @RequestParam(value = "flag", required = false, defaultValue = "0") int flag,
                     @RequestParam(value = "cut", required = false, defaultValue = "0") int cut,
                     HttpServletRequest request, HttpServletResponse response) throws Exception {
        String rootPath = ROOTPATH;
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(rootPath);
        stringBuilder.append(day);
        stringBuilder.append("/");
        stringBuilder.append(fileName);
        File file = new File(stringBuilder.toString());
        String contentType = Files.probeContentType(Paths.get(stringBuilder.toString()));
        response.addHeader("Content-Type", contentType);

        //固件bin文件支持断点续传
        if (fileName.endsWith(".bin")) {
            flag = 2;
        }
        //解决文件名称中文乱码
        fileName = new String(fileName.getBytes(), "ISO8859-1");

        //默认情况下非图片下载模式
        if (flag == 0) {
            if (!RequestUtil.isImage(file)) {
                response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
            }
        }
        //强制下载模式
        if (flag == 1) {
            response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
        }
        //流媒体文件
        if (flag == 2) {
            response.setHeader("Accept-Ranges", "bytes");
            String rangeHeader = request.getHeader(HttpHeaders.RANGE);
            long start, end;
            if (!StringUtils.isEmpty(rangeHeader)) {
                RandomAccessFile accessFile = new RandomAccessFile(file, "r");
                rangeHeader = rangeHeader.split("=")[1];
                String[] ranges = rangeHeader.split("-");
                start = Integer.valueOf(ranges[0]);
                if (start >= accessFile.length()) {
                    response.setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
                } else {
                    response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
                    if (ranges.length > 1) {
                        end = Integer.valueOf(rangeHeader.split("-")[1]);
                    } else {
                        end = accessFile.length() - 1;
                    }
                    accessFile.seek(start);
                    long len = end - start + 1;
                    byte[] bytes = new byte[Integer.valueOf(Long.valueOf(len).toString())];
                    int readLen = accessFile.read(bytes);
                    response.addHeader("Content-Length", String.valueOf(readLen));
                    response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + accessFile.length());
                    response.getOutputStream().write(bytes, 0, readLen);
                    response.getOutputStream().flush();
                }
                accessFile.close();
                return;
            }
        }
        try (InputStream is = new FileInputStream(file)) {
            //当传了宽高，截取中间部分
            if (cut > 0) {
                InputStream newIs = cutImage(ImageIO.read(file));
                response.addHeader("Content-Length", String.valueOf(newIs.available()));
                FileCopyUtils.copy(newIs, response.getOutputStream());
            } else {
                response.addHeader("Content-Length", String.valueOf(is.available()));
                FileCopyUtils.copy(is, response.getOutputStream());
            }
            response.getOutputStream().flush();
        }
    }

    /**
     * 长宽100%正方形截取图片
     */
    public InputStream cutImage(BufferedImage image) {
        //真实宽高
        int srcWidth = image.getWidth(null);
        int srcHeight = image.getHeight(null);
        //获取截取起点
        int startx = 0, starty = 0;
        int clip = srcWidth > srcHeight ? srcHeight : srcWidth;
        if (srcWidth > srcHeight) {
            startx = (srcWidth - srcHeight) / 2;
        } else {
            starty = (srcHeight - srcWidth) / 2;
        }
        // 截取图片
        Rectangle rect = new Rectangle(startx, starty, clip, clip);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImgUtil.cut(image, os, rect);
        return new ByteArrayInputStream(os.toByteArray());
    }

    @ApiOperation(value = "嵌入式bin文件上传", notes = "上传的bin文件名称进行特殊处理，文件名长度不超过30字节")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "前端框架文件ID", dataType = "string", paramType = "form", defaultValue = "11")
    })
    @ResponseBody
    @PostMapping(value = "/binUpload", consumes = "multipart/*", headers = "content-type=multipart/form-data")
    public AjaxResult binUpload(@ApiParam("上传的文件") MultipartFile file, @RequestParam("id") String fileId,
                                MultipartHttpServletRequest request) throws IOException {

        String newFileName = System.currentTimeMillis() + ".bin";
        SysCommonConfig config = sysCommonConfigMapper.findByKey("upload_file_save_path");
        if (config == null) return new AjaxResult(WamingConstant.FAILURE, "文件存储地址为空", null);
        String rootPath = config.getValue();
        if (rootPath == null) return new AjaxResult(WamingConstant.FAILURE, "文件存储地址为空", null);
        String path = "binfile";
        File dir = new File(rootPath + path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File newFile = new File(dir, newFileName);
        try (OutputStream os = new FileOutputStream(newFile)) {
            FileCopyUtils.copy(file.getInputStream(), os);
            os.flush();
        }
        String url = path + "/" + newFileName;
        Map<String, String> resultMap = new HashMap<>(3);
        resultMap.put("fileId", fileId);
        resultMap.put("urlPrefix", request.getContextPath() + "/file/view/");
        resultMap.put("url", url);
        //生产MD5码
        String md5 = DigestUtils.md5Hex(file.getInputStream());
        resultMap.put("md5", md5);
        return new AjaxResult(WamingConstant.SUCCESS, null, resultMap);
    }
}
